College Board
The following blog is intended to explain and provide the intended requirements for “Component A” of the Colelge Board “Create Performance Task” submission. The following bullet points are pulled from College Board instructions. My blog will go over these requirements.
- Use of at least one list (or other collection type) to represent a collection of data that is stored and used to manage program complexity and help fulfill the program’s purpose
- At least one procedure that contributes to the program’s intended purpose, where you have defined: the procedure’s name, the return type, one or more parameters
- algorithm that includes sequencing, seection, and interation that is in the body of the selected procedure
- calls to your student-developed procedure
- intructions for output
Introduction: Project Frostbyte
Purpose
- Provide a platform for nature and camping enthusiasts to share their experiences, learn about others’ experiences, and find resources for future adventures.
Individual Feature
- Post feed for camping page of Project Frostbyte
- Create posts, read other posts, update post feed with new posts, delete posts
Input/Output Requests (Live Demo, CB Requirement)
API Request (frontend)
Postman for raw API request and RESTful response
paramaters:
- user id, post title, comment, channel_id
response:
- channel_name Aquatic from channel_id 7
- comment camping! same as raw comment content
- id 3 for postId
- title test2 same as raw title content
- user_name Thomas Edison from id 1, the user id
Tester Data Creation (in model.camping and Frostbyte database)
def initCampingPosts():
with app.app_context():
"""Create database and tables"""
db.create_all()
"""Tester data for table"""
campingPosts = [
camping(title='Cool Experience', comment='aquatic camping was new and fun', user_id=1, channel_id=7),
camping(title='My next trip', comment='thinking of camping at the desert next', user_id=1, channel_id=8),
]
List Requests (CB Requirement)
Formatting response data (JSON) from API into DOM
async function fetchData(channelId) {
try {
const response = await fetch(`${pythonURI}/api/campingPosts/filter`, {
...fetchOptions,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel_id: channelId })
});
if (!response.ok) {
throw new Error('Failed to fetch posts: ' + response.statusText);
}
const postData = await response.json();
document.getElementById('count').innerHTML = `<h4>Total Posts: ${postData.length || 0}</h4>`;
const detailsDiv = document.getElementById('details');
detailsDiv.innerHTML = '';
postData.forEach(post => {
const postElement = document.createElement('div');
postElement.className = 'post-item';
postElement.style.marginBottom = "20px";
postElement.id = `post-${post.id}`;
// Create the delete button
const deleteButton = `<button onclick="deletePost(${post.id})">Delete</button>`;
postElement.innerHTML = `
<h3>${post.title}</h3>
<p style="font-size: 0.9rem; color: #000000;"><em>${post.user_name}</em></p>
<p>${post.comment}</p>
${deleteButton}
`;
postElement.innerHTML = `
<h3>${post.title}</h3>
<p style="font-size: 0.9rem; color: #000000;"><em>${post.user_name}</em></p>
<p>${post.comment}</p>
${deleteButton}
`;
detailsDiv.appendChild(postElement);
});
- converts raw json response into postData object
- created new div element postElement for eat post data in postData array
- html for posts are put into postElement using innerHTML
- appended to div after the posts are formatted
Extracting Python List. Queries are provided by a 3rd Party Library
campingPosts = camping.query.filter_by(_channel_id=data['channel_id']).all()
- from CampingAPI in class _FILTER
- get list of the rows from campingPosts table that follow the certain channel_id
- 3rd party library: SQLAlchemy
- SQLAlchemy provideds query, like the camping.query.filter to filter out certain posts based on the columns of table
Methods in class to work with columns (crud)
def __init__(self, title, comment, user_id=None, channel_id=None, user_name=None, channel_name=None):
self._title = title
self._comment = comment
self._user_id = user_id
self._channel_id = channel_id
- init in class camping model assigns values for the table’s columns
class _CRUD(Resource):
@token_required()
def post(self):
"""
Create a new post.
"""
# Obtain the current user from the token required setting in the global context
current_user = g.current_user
# Obtain the request data sent by the RESTful client API
data = request.get_json()
# Validate the presence of required keys
if not data:
return {'message': 'No input data provided'}, 400
if 'title' not in data:
return {'message': 'Post title is required'}, 400
if 'comment' not in data:
return {'message': 'Post comment is required'}, 400
if 'channel_id' not in data:
return {'message': 'Channel ID is required'}, 400
# Create a new post object using the data from the request
campingPost = camping(data['title'], data['comment'], current_user.id, data['channel_id'])
# Save the post object using the Object Relational Mapper (ORM) method defined in the model
campingPost.create()
# Return response to the client in JSON format, converting Python dictionaries to JSON format
return jsonify(campingPost.read())
- in class CampingAPI class _CRUD, the post method will create a new row in the campingPosts table
@token_required()
def get(self):
"""
Read post
"""
# Obtain and validate the request data sent by the RESTful client API
data = request.get_json()
if data is None:
return {'message': 'Post data not found'}, 400
if 'id' not in data:
return {'message': 'Post ID not found'}, 400
# Find the post to read
campingPost = camping.query.get(data['id'])
if campingPost is None:
return {'message': 'Post not found'}, 404
# Convert Python object to JSON format
json_ready = campingPost.read()
# Return a JSON restful response to the client
return jsonify(json_ready)
- method get will read data by turning it into json response
@token_required()
def put(self):
"""
Update
"""
# Obtain the current user
current_user = g.current_user
# Obtain the request data
data = request.get_json()
# Find the current post from the database table(s)
campingPost = camping.query.get(data['id'])
if campingPost is None:
return {'message': 'Post not found'}, 404
# Update the post
campingPost._title = data['title']
campingPost._channel_id = data['channel_id']
# Save the post
campingPost.update()
# Return response
return jsonify(campingPost.read())
- method put will update an existing post
@token_required()
def delete(self):
"""
Delete a post.
"""
# Obtain the current user
current_user = g.current_user
# Obtain the request data
data = request.get_json()
# Find the current post from the database table(s)
campingPost = camping.query.get(data['id'])
if campingPost is None:
return {'message': 'Post not found'}, 404
# Delete the post using the ORM method defined in the model
campingPost.delete()
# Return response
return jsonify({"message": "Post deleted"})
- method delete will delete post from data table using SQLAlchemy orm method
Algorithmic Code Requests (CB Requirements)
class _FILTER(Resource):
@token_required()
def post(self):
"""
Retrieve all posts by channel ID and user ID.
"""
# Obtain and validate the request data sent by the RESTful client API
data = request.get_json()
if data is None:
return {'message': 'Channel and User data not found'}, 400
if 'channel_id' not in data:
return {'message': 'Channel ID not found'}, 400
# Find all posts by channel ID and user ID
campingPosts = camping.query.filter_by(_channel_id=data['channel_id']).all()
# Prepare a JSON list of all the posts, using list comprehension
json_ready = [campingPost.read() for campingPost in campingPosts]
# Return a JSON list, converting Python dictionaries to JSON format
return jsonify(json_ready)
API class used to perform methods
- API class: CampingAPI
- class _CRUD used to create posts and delete posts
- class _FILTER used to fetch channel-filtered posts
Method/procedure containing sequencing, selection, and iteration
- sequencing (order): obtain user, request data, make sure all required parameters are filled out, create new post, return in json
- selection (decision making): determines whether to add post or display error message based on the if conditions
- iteration (repeating):
Parameters and return type
- parameters: title, comment, channel_id, user_id
- return jsonify converts dictionaries into json format. frontend will take data and display as html
Call to Algorithm Request
Call/request to method with Algorithm- fetching to endpoint
try {
const response = await fetch(`${pythonURI}/api/campingPost`, {
...fetchOptions,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData)
});
if (!response.ok) throw new Error('Failed to add post: ' + response.statusText);
alert("Post added successfully!");
await fetchData(channelID); // Refresh posts for the current channel
} catch (error) {
console.error('Error adding post:', error);
}
- sends request to POST method campingPost API endpoint
async function fetchData(channelId) {
try {
const response = await fetch(`${pythonURI}/api/campingPosts/filter`, {
...fetchOptions,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel_id: channelId })
});
if (!response.ok) {
throw new Error('Failed to fetch posts: ' + response.statusText);
}
- request to POST method to campingPosts/filter API endpoint: only fetches posts from channel that user posted to
- backend returns list of the posts under the channel_id (API response)